﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Basler.Pylon;
using OpenCvSharp;
using Forms = System.Windows.Forms;

namespace BenaGo.Helper
{
    public class BeanGoOpenCVHelper
    {

        public Scalar lineScaler = Scalar.White;

        public int lineThickness = 2;

        public LineTypes lineType = LineTypes.Link8;



        public enum BGColorConversionCodes
        {

            BGR2BGRA = 0,
            RGB2RGBA = 0,
            BGRA2BGR = 1,
            RGBA2RGB = 1,
            BGR2RGBA = 2,
            RGB2BGRA = 2,
            RGBA2BGR = 3,
            BGRA2RGB = 3,
            BGR2RGB = 4,
            RGB2BGR = 4,
            BGRA2RGBA = 5,
            RGBA2BGRA = 5,
            BGR2GRAY = 6,
            RGB2GRAY = 7,
            GRAY2BGR = 8,
            GRAY2RGB = 8,
            GRAY2BGRA = 9,
            GRAY2RGBA = 9,
            BGRA2GRAY = 10,
            RGBA2GRAY = 11,

        }

        /// <summary>
        /// 图片灰度化
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="colorConversionCodes"></param>
        /// <returns></returns>
        public Mat ToGrayWithImage(Mat srcImage,BGColorConversionCodes colorConversionCodes)
        {
            Mat resultImage = new Mat();

            if(srcImage != null)
            {
                Cv2.CvtColor(srcImage, resultImage, (ColorConversionCodes)colorConversionCodes);
                return resultImage;
            }

            return resultImage;
        }
    
        public Point MatchTemplateImage(string srcImgPath, string tempImgPath)
        {
            Mat srcImage = Cv2.ImRead(srcImgPath, ImreadModes.AnyColor);
            Mat tempImage = Cv2.ImRead(tempImgPath, ImreadModes.AnyColor);

            tempImage = this.ToGrayWithImage(tempImage, BGColorConversionCodes.RGB2GRAY);
            // 创建用于存储模板和匹配结果的图像
            Mat result = new Mat();

            // 使用模板匹配方法进行匹配，这里使用归一化相关系数匹配法
            Cv2.MatchTemplate(srcImage, tempImage, result, TemplateMatchModes.CCoeffNormed);

            // 获取匹配结果中的最小值、最大值以及对应的位置
            double minVal, maxVal;
            OpenCvSharp.Point minLoc, maxLoc;
            Cv2.MinMaxLoc(result, out minVal, out maxVal, out minLoc, out maxLoc);

            // 获取最佳匹配的位置
            OpenCvSharp.Point matchLoc = maxLoc;

            return matchLoc;
        }

        /// <summary>
        /// 图片高斯模糊
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public Mat GaussianBlurWithImage(Mat srcImage,OpenCvSharp.Size size)
        {
            Mat resultImage = new Mat();
            if(srcImage != null)
            {
                //Cv2.Blur(srcImage, resultImage, size);
                Cv2.GaussianBlur(srcImage, resultImage, size,4,4);
                return resultImage;
            }
            return resultImage;
        }

        /// <summary>
        /// 图片二值化
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="thresholdValue"></param>
        /// <param name="thresholdTypes"></param>
        /// <returns></returns>
        public Mat ThresholdWithImage(Mat srcImage,int thresholdValue,ThresholdTypes thresholdTypes)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                //Cv2.Blur(srcImage, resultImage, size);
                Cv2.Threshold(srcImage, resultImage, thresholdValue, 255, thresholdTypes);
                return resultImage;
            }
            return resultImage;
        }

        public Mat DilateWithImage(Mat srcImage,Mat element)
        {

            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                //Cv2.Blur(srcImage, resultImage, size);
                Cv2.Dilate(srcImage, resultImage,element);
                return resultImage;
            }
            return resultImage;

        }

        public Mat ErodeWithImage(Mat srcImage, Mat element)
        {

            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                //Cv2.Blur(srcImage, resultImage, size);
                Cv2.Erode(srcImage, resultImage, element);
                return resultImage;
            }
            return resultImage;

        }

        public Mat DilateErodeWithImage(Mat srcImage,Mat element)
        {
            Mat resultImage = new Mat();
            Mat erodeImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Dilate(srcImage, erodeImage, element);
                Cv2.Erode(erodeImage, resultImage, element);
                return resultImage;
            }
            return resultImage;
        }

        /// <summary>
        /// 图片旋转
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="angle"></param>
        /// <returns></returns>
        public Mat RotateAngleImage(Mat srcImage,double angle)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                float radian = (float)(angle / 180.0 * Cv2.PI);
                int maxBorder = (int)(Math.Max(srcImage.Cols, srcImage.Rows) * 1.414);
                int dx = (maxBorder - srcImage.Cols) / 2;
                int dy = (maxBorder - srcImage.Rows) / 2;
                Cv2.CopyMakeBorder(srcImage, resultImage, dy, dy, dx, dx, BorderTypes.Constant,OpenCvSharp.Scalar.White);
                


                //旋转
                Point2f center = new Point2f((float)(resultImage.Cols / 2), (float)(resultImage.Rows / 2));
                Mat affine_matrix = Cv2.GetRotationMatrix2D(center, angle, 1.0);//求得旋转矩阵
                Cv2.WarpAffine(resultImage, resultImage, affine_matrix, resultImage.Size());

                //计算图像旋转之后包含图像得最大的矩形
                double sinVal = Math.Abs(Math.Sin(radian));
                double cosVal = Math.Abs(Math.Cos(radian));

                Size targetSize = new Size((int)(srcImage.Cols* cosVal +srcImage.Rows * sinVal), (int)(srcImage.Cols * sinVal + srcImage.Rows * cosVal));

                //剪掉多余边框
                int x = (resultImage.Cols - targetSize.Width) / 2;
                int y = (resultImage.Rows - targetSize.Height) / 2;

                Rect rect = new Rect(x, y, targetSize.Width, targetSize.Height);

                resultImage = new Mat(resultImage, rect);
            }
            return resultImage;


        }


        public  Mat MyFindContours(Mat srcImage)
        {

            //转化为灰度图
            if (srcImage == null)
            {
                return null;
            }
            Mat src_gray = new Mat();

            Cv2.CvtColor(srcImage, src_gray, ColorConversionCodes.BGRA2GRAY);

            //滤波

            Cv2.Blur(src_gray, src_gray, new Size(3, 3));

            //Canny边缘检测

            Mat canny_Image = new Mat();

            Cv2.Canny(src_gray, canny_Image, 100, 200);

            //获得轮廓

            Point[][] contours;

            HierarchyIndex[] hierarchly;

            Cv2.FindContours(canny_Image, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new Point(0, 0));

            //将结果画出并返回结果

            Mat dst_Image = Mat.Zeros(canny_Image.Size(), srcImage.Type());

            Random rnd = new Random();

            for (int i = 0; i < contours.Length; i++)

            {

               Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));

                Cv2.DrawContours(dst_Image, contours, i, color, 2, LineTypes.Link8, hierarchly);

            }

            return dst_Image;

        }


        public List<Point[]>  MyFindContoursArea(Mat srcImage,double contoursAreaMinValue,double contoursAreaMaxValue,int ThresholdValue)
        {
            List<Point[]> contoursList = new List<Point[]>();

            if (srcImage == null)
                return null;

            Mat src_gray = new Mat();

            //Cv2.CvtColor(srcImage, src_gray, ColorConversionCodes.BGRA2GRAY);

            //滤波

            Cv2.Blur(srcImage, src_gray, new Size(3, 3));
           
            Point[][] contours = null;
            //获得轮廓

            Cv2.Threshold(src_gray, src_gray, ThresholdValue, 255, ThresholdTypes.Binary);

            //Cv2.ImShow("src_gray", src_gray);

            HierarchyIndex[] hierarchly;

            Cv2.FindContours(src_gray, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);

            

            for (int i = 0; i < contours.Length; i++)
            {

                Console.WriteLine("面积：" + Cv2.ContourArea(contours[i]));

                if(Cv2.ContourArea(contours[i]) > contoursAreaMinValue && Cv2.ContourArea(contours[i]) < contoursAreaMaxValue)
                {

                    double e = Cv2.ContourArea(contours[i]);

                    contoursList.Add(contours[i]);
                    //Cv2.DrawContours(srcImage, contours, i, OpenCvSharp.Scalar.White, 2, LineTypes.Link8, hierarchly);
                }

            }

            return contoursList;

        }

        public Point3f MyGetMinAreaRectangle(List<Point[]> contours)
        {
            Point3f point = new Point3f();
            float degree = 0;
            float maxw = 0;
            float maxh = 0;
            for (int t = 0; t < contours.Count(); t++)
            {
                Array array = contours.ToArray();
                RotatedRect minRect = Cv2.MinAreaRect(contours.ToArray()[t]);// minAreaRect(contours[t]);
                degree = minRect.Angle;

                maxw = minRect.Size.Width;
                maxh = minRect.Size.Height;

                point.X = maxw;
                point.Y = maxh;
                point.Z = degree;

            }
            return point;
        }

        public Rect2f MyGetMinAreaRectangleRect2f(List<Point[]> contours)
        {
            Rect2f rect= new Rect2f();

            for (int t = 0; t < contours.Count(); t++)
            {
                Array array = contours.ToArray();
                RotatedRect minRect = Cv2.MinAreaRect(contours.ToArray()[t]);// minAreaRect(contours[t]);
                rect.X = minRect.Center.X; rect.Y = minRect.Center.Y; rect.Width = minRect.Size.Width; rect.Height = minRect.Size.Height;

                
            }
            return rect;
        }


        public Point3d FindPicFromImage(Mat srcMat, Mat dstMat, double threshold = 0.9)
        {

            OpenCvSharp.OutputArray outArray = null;
            try
            {
                srcMat = srcMat.CvtColor(ColorConversionCodes.RGBA2RGB);

                outArray = OpenCvSharp.OutputArray.Create(srcMat);

                OpenCvSharp.Cv2.MatchTemplate(srcMat, dstMat, outArray, TemplateMatchModes.CCoeffNormed);

                double minValue, maxValue;
                OpenCvSharp.Point location, point;
                OpenCvSharp.Cv2.MinMaxLoc(OpenCvSharp.InputArray.Create(outArray.GetMat()), out minValue, out maxValue, out location, out point);
                
                if (maxValue >= threshold)
                    return new OpenCvSharp.Point3d(point.X, point.Y, maxValue);
                return new OpenCvSharp.Point3d(0, 0, 0);
            }
            catch (Exception ex)
            {
                return new OpenCvSharp.Point3d(0, 0, 0);
            }
            finally
            {

            }
        }

        public Mat MyDrawBoundRectangle(List<Point[]> contours,Mat srcImage)
        {
            Mat resultImage = new Mat();

            resultImage = srcImage.Clone();

            Rect[] boundRect = new Rect[contours.Count];//= new Rect[]();

            Rect rect = new Rect();

            if (srcImage != null)
            {
                for (int i = 0; i < contours.Count; i++) {
                    boundRect[i] = Cv2.BoundingRect(contours.ToArray()[i]);

                    rect.X = boundRect[i].X;
                    rect.Y = boundRect[i].Y;
                    rect.Width = boundRect[i].Width;
                    rect.Height = boundRect[i].Height;

                    Cv2.Rectangle(resultImage, rect, Scalar.Red, 2);

                }
                
            }
            return resultImage;


        }
        public Mat GetTwoImageABSDIFF(Mat srcImage,Mat dstImage)
        {
            Mat resultImage = new Mat();

            Mat grayImage1 = new Mat(); Mat grayImage2 = new Mat();
            Mat thresholdImage1 = new Mat(); Mat thresholdImage2 = new Mat();

            Cv2.CvtColor(srcImage, grayImage1, ColorConversionCodes.BGRA2GRAY);
            Cv2.CvtColor(dstImage, grayImage2, ColorConversionCodes.BGRA2GRAY);

            Cv2.Threshold(grayImage1, thresholdImage1, 100, 255, ThresholdTypes.Binary);
            Cv2.Threshold(grayImage2, thresholdImage2, 100, 255, ThresholdTypes.Binary);

            Mat dst = new Mat();
            Cv2.Absdiff(thresholdImage1, thresholdImage2, dst);
            Cv2.BitwiseNot(dst,resultImage);

            return resultImage;
        }

        /// <summary>
        /// 在图片上绘制椭圆
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="centerPoint"></param>
        /// <param name="size"></param>
        /// <param name="ellipseAngle"></param>
        /// <returns></returns>
        public Mat DrawEllipse(Mat srcImage,Point centerPoint,Size size,double ellipseAngle)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Ellipse(srcImage, centerPoint, size, ellipseAngle, 0, 360, this.lineScaler, this.lineThickness, this.lineType);

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }


        /// <summary>
        /// 在图片上绘制圆
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="centerPoint"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        public Mat DrawCircle(Mat srcImage, Point centerPoint, int radius)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Circle(srcImage,centerPoint, radius, this.lineScaler, this.lineThickness, this.lineType);

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }

        /// <summary>
        /// 在图片上绘制直线
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="startP"></param>
        /// <param name="endP"></param>
        /// <returns></returns>
        public Mat DrawLine(Mat srcImage,Point startP,Point endP)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Line(srcImage, startP, endP, this.lineScaler, this.lineThickness, this.lineType);

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }

        /// <summary>
        /// 在图片上绘制矩形根据Rect
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        public Mat DrawRectangleForRect(Mat srcImage, Rect rect)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Rectangle(srcImage, rect, this.lineScaler, this.lineThickness, this.lineType);

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }

        /// <summary>
        /// 在图片上绘制直线根据对角点
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="startP"></param>
        /// <param name="endP"></param>
        /// <returns></returns>
        public Mat DrawRectangleForPoint(Mat srcImage, Point startP, Point endP)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                Cv2.Rectangle(srcImage, startP, endP, this.lineScaler, this.lineThickness, this.lineType);

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }

        /// <summary>
        /// 在图片上绘制文字
        /// </summary>
        /// <param name="srcImage"></param>
        /// <param name="text"></param>
        /// <param name="org"></param>
        /// <param name="isSuccessFlag"></param>
        /// <returns></returns>
        public Mat DrawPutText(Mat srcImage,string text,Point org,bool isSuccessFlag)
        {
            Mat resultImage = new Mat();
            if (srcImage != null)
            {
                if (isSuccessFlag)
                {
                    Cv2.PutText(srcImage, text, org, HersheyFonts.HersheySimplex, 1, Scalar.Green, this.lineThickness, this.lineType);
                }
                else
                {
                    Cv2.PutText(srcImage, text, org, HersheyFonts.HersheySimplex, 1, Scalar.Red, this.lineThickness, this.lineType);
                }

                resultImage = srcImage.Clone();
            }
            return resultImage;
        }


        #region 保存图像
        private bool SaveImage(Mat srcImage,string savePath)
        {
            bool iSSaveSuccess = true;

            try
            {
                srcImage.SaveImage(savePath);

                return iSSaveSuccess;

            }catch(Exception e)
            {
                return false;
            }

        }

        public bool SaveImageForDateTimeNow(Mat srcImage,string Extension)
        {
            string SavePath = System.AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_ffff") + Extension;

            return SaveImage(srcImage, SavePath);
        }

        public bool SaveImageForDialog(Mat srcImage)
        {
            //创建一个保存文件式的对话框
            Forms.SaveFileDialog sfd = new Forms.SaveFileDialog();

            //设置这个对话框的起始保存路径
            sfd.InitialDirectory = @"D:\";

            //设置保存的文件的类型，注意过滤器的语法
            sfd.Filter = ".png|*.png|.jpeg|*.jpeg|.bmp|*.bmp|All files (*.*)|*.*";

            //调用ShowDialog()方法显示该对话框，该方法的返回值代表用户是否点击了确定按钮
            if (sfd.ShowDialog() == Forms.DialogResult.OK)
            {
                //做一些工作 　　　　　　　　　　　　
                return SaveImage(srcImage, sfd.FileName);
            }
            else
            {
               
                return false;
            }

        }

        #endregion


    }
}
